package client;

import capsules.CapsuleSpecification;
import capsules.CapsuleState;
import communication.ConnectionException;
import constans.CapsuleConstans;
import constans.ConfigurationConstans;
import constans.SizeConstans;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import utils.MachineUtils;

public class RecoveryProcess extends Process{


    public RecoveryProcess(RarcServer rarcServer, Configuration configuration,
            Logger logger, AtomicBoolean isExecute ){
        super.rarcServer = rarcServer;
        super.configuration = configuration;
        super.isExecute = isExecute;
        super.logger = logger;
    }



    public void execute()
            throws ConnectionException, GeneralSecurityException{

        loadCapsules();
        logger.log(Level.INFO,"Load capsules: "+capsules.size()+" capsules");

		synchronizedRegister();
        logger.log(Level.INFO,"Synchronized Register");


        rarcServer.sendOfferedSpace(configuration.getOfferSpace());
        logger.log(Level.INFO,"SEND: Offered Space("+configuration.getOfferSpace()+" bytes)");

       if(capsules!=null)

		for(String capsule:capsules){

			if(isExecute.get()){
                try {

                    long length = MachineUtils.getFileLength(ConfigurationConstans.LOCAL_DIRECTORY, capsule);
                    byte[] hash = MachineUtils.getHashFile(ConfigurationConstans.LOCAL_DIRECTORY, capsule);

                    CapsuleSpecification capsuleSpecification = new CapsuleSpecification(capsule,
                            length, hash, register.getOffset(capsule));

                    CapsuleState capsuleState = rarcServer.getCapsuleState(capsuleSpecification);

                    if (capsuleState.equals(CapsuleState.INCORRECT)) {
                        MachineUtils.deleteFile(ConfigurationConstans.LOCAL_DIRECTORY, capsule);
                        logger.log(Level.INFO,"State ["+capsuleSpecification.getName()+"] -> Delete("+capsuleState+")");
                    }else if(capsuleState.equals(CapsuleState.INCOMPLETE)) {
                        logger.log(Level.INFO,"State ["+capsuleSpecification.getName()+"] -> Do nothing("+capsuleState+")");
                    }else{
                          logger.log(Level.INFO,"State ["+capsuleSpecification.getName()+"] -> Upload("+capsuleState+")");

                          if(rarcServer.verifyUploadCapsule(capsuleSpecification.getName())){
                            long offsetCapsule = rarcServer.uploadCapsule(capsuleSpecification.getName());
                            logger.log(Level.INFO,"Recovery-> offsetCapsule:"+offsetCapsule);
                            doUpload(capsuleSpecification.getName(),offsetCapsule);
                          }else{
                                logger.log(Level.INFO,"Not Recovery");
                          }
                  }
                } catch (IOException ex) {
                    logger.log(Level.WARNING,ex.getMessage());
                }
            }else{
                break;
			}
		}
    }


    private void loadCapsules() {

        String [] filesnames = MachineUtils.getFilesNames(ConfigurationConstans.LOCAL_DIRECTORY,
        CapsuleConstans.CAPSULE_EXTENSION);


        if(filesnames!=null){
            capsules = new ArrayList<String>();
            for(int i = 0;i < filesnames.length ;i++){
                capsules.add(filesnames[i]);
            }
        }else{
            capsules = null;
        }
    }


	private void synchronizedRegister() {

        try {
            register = new Register();
            register.load(ConfigurationConstans.REGISTER_FILENAME);
        } catch (IOException e){
            logger.log(Level.WARNING,e.getMessage());
        }

		Register registerTemp = new Register();

		long offset;

        if(capsules!=null){

            for(int i = 0; i < capsules.size() ; i++){

                try{
                    if( ( offset = register.getOffset(capsules.get(i))) < 0)
                        offset = 0;

                    registerTemp.insert(capsules.get(i), offset);
                } catch (NumberFormatException e) {
                    registerTemp.insert(capsules.get(i),0L);
                }
            }
        }

        register = registerTemp;
	}


    private void doUpload(String capsule, long offset)
            throws ConnectionException, FileNotFoundException,
            IOException{

		RandomAccessFile capsuleStream = null;
		try{

			capsuleStream = new RandomAccessFile(capsule, "r");
			capsuleStream.seek(offset);

            int numRead = 0;
			byte[] buffer = new byte[(int)SizeConstans.KByte];

			while ( (numRead = capsuleStream.read(buffer, 0, buffer.length)) > 0 && isExecute.get())
                rarcServer.sendCapsuleBytes(buffer, 0, numRead);

            capsuleStream.close();

		} catch (FileNotFoundException e) {
			try{
				if(capsuleStream!=null){
					capsuleStream.close();
					capsuleStream = null;
				}
			}catch(IOException e1){}
			throw new FileNotFoundException(e.getMessage());
		} catch (ConnectionException e) {
			try{
				if(capsuleStream!=null){
					capsuleStream.close();
					capsuleStream = null;
				}
			}catch(IOException e1){}
			throw new ConnectionException(e.getMessage());
		} catch (IOException e) {
			try{
				if(capsuleStream!=null){
					capsuleStream.close();
					capsuleStream = null;
				}
			}catch(IOException e1){}
			throw new IOException();
		} catch (IndexOutOfBoundsException e) {
			try{
				if(capsuleStream!=null){
					capsuleStream.close();
					capsuleStream = null;
				}
			}catch(IOException e1){}
			throw new ConnectionException(e.getMessage());
		}
	}

}
